/******************************************************************************************************
Copyright (C) 2018 Mestrelab Research S.L. All rights reserved.

This file is part of the MNova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*****************************************************************************************************/

/*globals gc, mainWindow, molecule, qsTr, settings, Application, BinaryStream, CommonSlots, Document, File, FileInfo, MessageBox, Pixmap, ProgressDialog, SDFBrowser, SDFBrowserCreate, Str, TextStream*/
/*jslint plusplus: true, continue: true, bitwise: true*/


Application.mainWindow.registerDockWidget("SDFBrowser", qsTr("SDF Browser"), "Molecule", SDFBrowserCreate);

function SDFBrowserCreate() {
	"use strict";
	this.browser = new SDFBrowser();
	if (this.browser.dialog === undefined) {
		this.browser.dialog = Application.loadUiFile("ricares:SDFBrowser.ui");
		this.browser.connectHandlers();
		this.browser.disableCursor();
		this.browser.disableFilter();
		this.browser.getSettings();
		this.browser.currentFile = new File(this.browser.dialog.widgets.leSDFile.text);
		if (this.browser.molCount > 0) {
			this.browser.loadImage();
		}
	}
	return this.browser.dialog;
}

function SDFBrowser() {
	"use strict";

	this.defineKeys();
	this.defineData();
}

SDFBrowser.prototype.regexIndexOf = function (aString, aRegex, aStartpos) {
	"use strict";

	var indexOf, result;

	indexOf = aString.substring(aStartpos || 0).search(aRegex);
	result = (indexOf >= 0) ? (indexOf + (aStartpos || 0)) : indexOf;

	return result;
};

SDFBrowser.prototype.defineData = function () {
	"use strict";

	this.currentMolecule = 1;
	this.fChecked = false;
	this.resizeImage = false;
	this.filterEnabled = false;
	this.bufferSize = 33554432; //2^25 = 32MB
	this.endingTokenMolecule = "$$$$";
	this.endingTokenMoleculeRegex = /\$\$\$\$(\s)+/g;
	this.tagBeginingRegex = />\s(\s)?</g;
};

SDFBrowser.prototype.defineKeys = function () {
	"use strict";

	this.lastFilePathKey = "SDFBrowser/Last File Path";
	this.fitImageKey = "SDFBrowser/Fit Image";
};

SDFBrowser.prototype.connectHandlers = function () {
	"use strict";

	this.dialog.widgets.btPaste.clicked.connect(this, function () {
		this.pasteMolecule();
	});

	this.dialog.widgets.btSDFile.clicked.connect(this.dialog.widgets.leSDFile, CommonSlots.onOpenFile);

	this.dialog.widgets.btLeftMost.clicked.connect(this, function () {
		this.disableCursor();
		this.getFirstMolecule();
		this.enableCursor();
	});

	this.dialog.widgets.btLeft.clicked.connect(this, function () {
		this.disableCursor();
		this.getPreviousMolecule();
		this.enableCursor();
	});

	this.dialog.widgets.btRight.clicked.connect(this, function () {
		this.disableCursor();
		this.getNextMolecule();
		this.enableCursor();
	});

	this.dialog.widgets.btRightMost.clicked.connect(this, function () {
		this.disableCursor();
		this.getLastMolecule();
		this.enableCursor();
	});

	this.dialog.widgets.btFilter.clicked.connect(this, function () {
		if (this.dialog.widgets.leFilter.text !== "") {
			this.filterEnabled = true;
			if (this.filter()) {
				this.enableCursor();
			} else {
				this.cleanDisplay();
				this.disableCursor();
			}
		} else {
			this.filterEnabled = false;
			this.filteredMoleculesPosition = [];
			if ((this.lastSDFFileParsed !== undefined) && (this.dialog.widgets.leSDFile.text.trim() !== "") && (this.lastSDFFileParsed.file === this.dialog.widgets.leSDFile.text)) {
				this.moleculesPosition = this.lastSDFFileParsed.data;
				this.currentFile = new File(this.lastSDFFileParsed.file);
				this.molCount = this.moleculesPosition.length;
				if (this.molCount !== undefined) {
					this.dialog.widgets.gbResults.title = qsTr("Found {0} molecules").format(this.molCount);
					this.currentMolecule = 1;
					this.loadMolecule();
					this.enableCursor();
				}
			} else if (this.loadSDFile()) {
				this.enableCursor();
			}
		}
	});

	this.dialog.widgets.leMoleculeIndex.editingFinished.connect(this, function () {
		this.disableCursor();
		if (!this.getMoleculeByPosition(parseInt(this.dialog.widgets.leMoleculeIndex.text, 10))) {
			if (this.filteredMoleculesPosition !== undefined && this.filteredMoleculesPosition.length > 0) {
				this.dialog.widgets.leMoleculeIndex.text = this.currentMolecule;
			} else {
				this.dialog.widgets.leMoleculeIndex.text = this.currentMoleculeFiltered;
			}
		}
		this.enableCursor();
	});

	this.dialog.widgets.leSDFile.textChanged.connect(this, function () {
		if (this.moleculesPosition !== undefined) {
			this.molCount = this.moleculesPosition.length;
		} else {
			this.molCount = 0;
		}
		this.disableCursor();
		this.disableFilter();
		this.filterEnabled = false;
		this.filteredMoleculesPosition = [];
		if (this.loadSDFile()) {
			this.enableCursor();
			this.enableFilter();
		} else {
			this.dialog.widgets.gbResults.title = qsTr("Found 0 molecules");
		}
	});

	this.dialog.widgets.chbxFitImage.clicked.connect(this, function () {

		settings.setValue(this.fitImageKey, this.dialog.widgets.chbxFitImage.checked);
		this.resizeImage = this.dialog.widgets.chbxFitImage.checked;
		this.dialog.widgets.lbCurrentMoleculeImage.pixmap = undefined;
		this.dialog.widgets.lbCurrentMoleculeImage.text = "";
		this.dialog.widgets.lbCurrentMoleculeImage.update();
		this.loadImage();
	});

};

SDFBrowser.prototype.loadSDFile = function () {
	"use strict";


	this.currentFile = new File(this.dialog.widgets.leSDFile.text);
	settings.setValue(this.lastFilePathKey, this.dialog.widgets.leSDFile.text);
	this.cleanDisplay();

	if (this.currentFile.exists && (new FileInfo(this.currentFile.name).isFile)) {
		this.moleculesPosition = this.parseSDFFile(this.currentFile.name);
		this.molCount = this.moleculesPosition.length;

		if (this.molCount !== undefined) {
			this.dialog.widgets.gbResults.title = qsTr("Found {0} molecules").format(this.molCount);
			this.currentMolecule = 1;

			if (!this.filterEnabled) {
				this.lastSDFFileParsed = {};
				this.lastSDFFileParsed.file = this.dialog.widgets.leSDFile.text;
				this.lastSDFFileParsed.data = this.moleculesPosition;
			}

			return this.loadMolecule();
		}
	}

};

SDFBrowser.prototype.getImageFittedToLabel = function (aFilePathImage, aLabel, aPixmap, aResize, aWidthResize, aHeightResize) {
	"use strict";

	var pixmap, resize, resizeWidth, resizeHeight;

	pixmap = new Pixmap();
	resize = 1;

	//Get the specified pixmap if exists
	if (aPixmap !== undefined) {
		pixmap = aPixmap;
	} else {
		pixmap.load(aFilePathImage);
	}

	//Get constant for each dimension
	if (aResize !== undefined && aResize && aWidthResize !== undefined && aHeightResize !== undefined) {
		if (pixmap.width > aWidthResize) {
			resizeWidth = Number(aWidthResize) / Number(pixmap.width);
			resize = resizeWidth;
		}
		if (pixmap.height > aHeightResize) {
			resizeHeight = Number(aHeightResize) / Number(pixmap.height);
			resize = resizeHeight;
		}

		//Get the lower real number constant to scale both dimensions
		if (resizeHeight !== undefined && !isNaN(resizeHeight) && (resizeHeight < resizeWidth)) {
			resize = resizeHeight;
		} else if (resizeWidth !== undefined && !isNaN(resizeWidth) && (resizeHeight >= resizeWidth)) {
			resize = resizeWidth;
		}
	}

	//Decide if we need to scale image or it doesn't make sense.
	if (resize !== undefined && resize !== 1 && aResize !== undefined && aResize) {
		aLabel.pixmap = pixmap.scaled(pixmap.width * resize, pixmap.height * resize, 1, 1);
	} else {
		aLabel.pixmap = pixmap;
	}
};

SDFBrowser.prototype.getMoleculeImage = function (aImagePath, aResize, aPixmap) {
	"use strict";

	this.getImageFittedToLabel(aImagePath, this.dialog.widgets.lbCurrentMoleculeImage, aPixmap, aResize, this.dialog.widgets.scrollAreaStructureImage.width, this.dialog.widgets.scrollAreaStructureImage.height);
};

SDFBrowser.prototype.loadImage = function () {
	"use strict";

	if (this.filteredMoleculesPosition !== undefined && this.filteredMoleculesPosition.length > 0) {
		this.dialog.widgets.leMoleculeIndex.text = parseInt(this.currentMoleculeFiltered, 10);
	} else {
		this.dialog.widgets.leMoleculeIndex.text =  parseInt(this.currentMolecule, 10);
	}
	this.getMoleculeImage(undefined, this.resizeImage, this.moleculePixmap);
	this.getSDFTags();
	mainWindow.processEvents();

};

SDFBrowser.prototype.cleanDisplay = function () {
	"use strict";

	this.dialog.widgets.lbCurrentMoleculeImage.text = "";
	this.dialog.widgets.leMoleculeIndex.text = 0;
	this.dialog.widgets.leSDFData.plainText = "";
	this.dialog.widgets.lbCurrentMoleculeImage.pixmap = undefined;
	this.moleculePixmap = undefined;
};

SDFBrowser.prototype.loadMolecule = function () {
	"use strict";

	var doc, activeMolecule, moleculeContent, moleculeUUID;

	//We load the molecule from the SDF file
	moleculeContent = this.getMoleculeContent();
	this.disableCursor();

	//We load molecule if we have data
	if (moleculeContent !== undefined) {

		//It creates the auxiliary document in order to create the molecule image
		//Calling this constructor the document will be hidden unless we call to the method mainWindow.addDocument
		doc = new Document();

		//It locks the document to ensure all pointers like mainWindow.activeDocument and activeMolecule() points to the hidden document
		Application.lockDocument(doc);

		//We must create a page before using the molecule.importMolfile(moleculeContent).
		//If we had used Serialization.open this instruction would have created the page automatically
		doc.newPage();
		moleculeUUID = molecule.importMolfile(moleculeContent);

		//We create the pixmap in the hidden document using the active molecule
		activeMolecule = doc.getItem(moleculeUUID);
		if (activeMolecule !== undefined && activeMolecule.uuid !== undefined) {
			this.moleculePixmap = activeMolecule.toPixmap();
		}

		//Destroy the hidden document
		doc.destroy();

		//It unlocks the auxiliary document in order to restore the pointers
		Application.unlockDocument();

		//It loads the molecule image in the label
		this.resizeImage = this.dialog.widgets.chbxFitImage.checked;
		this.dialog.widgets.lbCurrentMoleculeImage.pixmap = undefined;
		this.dialog.widgets.lbCurrentMoleculeImage.text = "";
		this.dialog.widgets.lbCurrentMoleculeImage.update();
		this.loadImage();

	} else {
		this.cleanDisplay();
		return false;
	}

	return true;
};

SDFBrowser.prototype.parseSDFFile = function (aPathFile) {
	"use strict";

	var progressDialog, inFile, inFileName, inStream, line, endMoleculePosition, moleculePosition, found, match, position;

	//Initialize progress dialog which is neede for huge files above all
	progressDialog = new ProgressDialog();
	progressDialog.mimum = 0;
	progressDialog.maximum = 100;
	progressDialog.value = 0;
	progressDialog.visible = true;

	if (aPathFile !== undefined && File.exists(aPathFile)) {
		inFileName = aPathFile;
	} else {
		inFileName = this.currentFile.name;
	}
	inFile = new File(this.dialog.widgets.leSDFile.text);
	if (!inFile.open(File.ReadOnly)) {
		MessageBox.critical("Could not open file '" + inFileName + "'.");
		return;
	}

	//Initialize stream, buffer and other vars
	inStream = new BinaryStream(inFile);
	line = "";
	endMoleculePosition = 0;
	moleculePosition = [];
	found = false;

	while (!inStream.atEnd() && !progressDialog.wasCanceled) {

		//Split by new lines reading byte by byte
		while (!inStream.atEnd() && (progressDialog === undefined || !progressDialog.wasCanceled)) {

			//Read a block file defined by the buffer size
			line += inStream.readBytes(this.bufferSize);
			progressDialog.raise();

			//Get ocurrences and theirs position
			do {
				match = this.endingTokenMoleculeRegex.exec(line);

				//Perform actioms for each match
				if (match) {
					//Add the positiom of the occurrence
					position = inStream.pos - (line.length - match.index);
					moleculePosition.push(Math.abs(position));
					found = true;
				}
			} while (match);

			if (found) {

				//Reset vars
				if (!inStream.atEnd()) {
					line = "";
				}
				found = false;

				//Update progress bar information
				progressDialog.labelText = qsTr("Parsing SDfile: {0} molecules found ").format(moleculePosition.length);
				progressDialog.value = (inStream.pos / inFile.size) * 100;
			}
		}
	}

	//Check for a single molecule without carriadge return
	if (inStream.atEnd() && line.length > 4) {
		if (line.substring(line.length - this.endingTokenMolecule.length) === this.endingTokenMolecule) {
			moleculePosition.push(inFile.size);
		}
	}

	// All the parsing process was done
	progressDialog.value = 100;
	progressDialog.visible = false;
	inFile.close();
	line = undefined;
	gc();

	return moleculePosition;
};

SDFBrowser.prototype.getSDFTags = function () {
	"use strict";

	var html, startIndex, finalIndex, nextIndex, tagname, value, line, index, lastIndex, s, moleculeContent, tags, auxValue;

	moleculeContent = this.getMoleculeContent();
	tags = [];

	if (moleculeContent !== undefined) {

		line = moleculeContent;
		index = line.indexOf("M  END");

		if (index !== -1) {
			lastIndex = line.length;

			s = line.substring(index + 6, lastIndex + 4);

			startIndex = 0;
			while (startIndex < s.length && startIndex !== -1) {
				startIndex =  this.regexIndexOf(s, this.tagBeginingRegex, startIndex);
				finalIndex = s.indexOf(">", startIndex + 4);

				if (startIndex !== -1) {
					//It reduces a position in case of single-spaced tags
					if (s.substring(startIndex + 1, startIndex + 3) !== "  ") {
						startIndex--;
					}
					tagname = s.substring(startIndex + 4, finalIndex);
					startIndex = finalIndex + 1;
					finalIndex = this.regexIndexOf(s, this.tagBeginingRegex, startIndex + 4);
					nextIndex = s.indexOf(this.endingTokenMolecule, startIndex + 4);

					if (finalIndex === -1) {
						if (nextIndex === -1) {
							finalIndex = s.length;
						} else {
							finalIndex = nextIndex;
						}
					} else if (nextIndex > 0 && nextIndex < finalIndex) {
						finalIndex = nextIndex;
					}
					value = s.substring(startIndex, finalIndex);
					if (value.length > 103) {
						value = value.substring(0, 100) + "...";
					}
					tags.push([tagname, value]);
					startIndex = finalIndex;
				}

			}
		}
	}
	html = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"><html><head></head><body>';

	for (index = 0; index < tags.length; index++) {

		tagname = Str.specialCharactersToXMLEntities(tags[index][0]);

		//Remove Mnova version in the Mnova_IUPAC_Name tag created by the iupacNameValidation tool
		if (tagname === SDFBrowser.mnovaIUPACTagName) {
			auxValue = Str.specialCharactersToXMLEntities(tags[index][1]);

			//The regex takes into account a new possible version notation in the revision tag if we use the git version control system in the future
			/*jslint regexp: true */
			value = auxValue.replace(/\s?mestrenova\s.+\sv\.\d+\.\d+\.\d+-[0-9a-f]+/gi, "");
			/*jslint regexp: false */

		} else {
			value = Str.specialCharactersToXMLEntities(tags[index][1]);
		}

		html += '<b>' +  tagname + ':</b> ' + value + '<br>';
	}
	html += '</body></html>';

	this.dialog.widgets.leSDFData.html = html;

};

SDFBrowser.prototype.enableCursor = function () {
	"use strict";
	this.dialog.widgets.gbResults.enabled = true;
	//this.dialog.widgets.gbFilter.enabled = true;
};


SDFBrowser.prototype.enableFilter = function () {
	"use strict";
	this.dialog.widgets.gbFilter.enabled = true;
};


SDFBrowser.prototype.disableFilter = function () {
	"use strict";
	this.dialog.widgets.gbFilter.enabled = false;
};

SDFBrowser.prototype.getRegularExpresion = function () {
	"use strict";
	var str = this.dialog.widgets.leFilter.text;
	str = str.split(" ");
	return str;
};

SDFBrowser.prototype.filter = function () {
	"use strict";

	var i, j, str, matched, exp, currentExpresion, progressDialog;

	try {

		//Initialize vars
		exp = this.getRegularExpresion();
		this.filteredMoleculesPosition = [];

		//Initialize progress dialog. This will let the users cancel the process
		progressDialog = new ProgressDialog();
		progressDialog.mimum = 0;
		progressDialog.maximum = 100;
		progressDialog.value = 0;
		progressDialog.visible = true;

		//Create an extra mapping array for all matched molecules
		for (i = 0; i < this.moleculesPosition.length && !progressDialog.wasCanceled; i++) {
			str = this.getMoleculeContent(i);
			matched = true;
			j = 0;
			while (j < exp.length && matched) {
				currentExpresion = new RegExp(exp[j], "i");
				matched = str.match(currentExpresion);
				j++;
			}
			if (matched) {
				this.filteredMoleculesPosition.push(i + 1);
			}
			progressDialog.labelText = qsTr("Filtering molecules: {0} molecules found ").format(this.filteredMoleculesPosition.length);
			progressDialog.value = ((i + 1) / this.moleculesPosition.length) * 100;
			mainWindow.processEvents();
		}

		//Remove progress dialog
		progressDialog.value = 100;
		progressDialog.visible = false;

		this.molCount = this.filteredMoleculesPosition.length;
		this.dialog.widgets.gbResults.title = qsTr("Found {0} molecules matching: {1}").format(this.molCount, exp.toString());
		if (this.molCount > 0) {
			this.currentMoleculeFiltered = 1;
			this.currentMolecule = this.filteredMoleculesPosition[this.currentMoleculeFiltered - 1];
		} else {
			return false;
		}
		mainWindow.processEvents();
		return this.loadMolecule();
	} catch (ERR) {
		MessageBox.critical(ERR);
	}
};

SDFBrowser.prototype.disableCursor = function () {
	"use strict";

	this.dialog.widgets.gbResults.enabled = false;
};

SDFBrowser.prototype.getMoleculeContent = function (aIndexMolecule) {
	"use strict";

	var indexMoleculeArray, content, position, bytesNeeded, file, binaryStream, result;

	result = undefined;
	if (File.exists(this.currentFile.name)) {

		if (this.moleculesPosition !== undefined && this.moleculesPosition.length > 0) {

			//Get the SDF File and the current index for the curren molecule
			file = this.currentFile;
			indexMoleculeArray = this.currentMolecule - 1;

			if (aIndexMolecule !== undefined) {
				indexMoleculeArray = aIndexMolecule;
			}

			//Get the positions to retrieve the part of the file what is needed
			if (indexMoleculeArray === 0) {
				position = 0;
				bytesNeeded = this.moleculesPosition[indexMoleculeArray];
			} else if (indexMoleculeArray === (this.moleculesPosition.length - 1)) {
				position = this.moleculesPosition[indexMoleculeArray - 1];
				bytesNeeded = Math.abs(file.size - position) + 1;
			} else {
				position = this.moleculesPosition[indexMoleculeArray - 1];
				bytesNeeded = Math.abs(this.moleculesPosition[indexMoleculeArray] - position);
			}

			//Get the piece of file what is needed
			file.open(File.ReadOnly);
			binaryStream = new BinaryStream(file);
			binaryStream.pos = position;
			content = binaryStream.readBytes(bytesNeeded);
			file.close();

			//Get the final data
			result = content.toString("utf-8").replace(/\$\$\$\$(\s)?/g, "");

		} else {
			MessageBox.critical("No molecules found");
		}

	} else {
		MessageBox.critical("Could not open file '" + this.dialog.widgets.leSDFile.text + "'.");
	}

	return result;
};

SDFBrowser.prototype.getSettings = function () {
	"use strict";

	var tempFile, data;

	tempFile = new File(settings.value(this.lastFilePathKey, ""));
	if (tempFile.exists) {
		this.dialog.widgets.leSDFile.text = settings.value(this.lastFilePathKey, "").trim();
	} else {
		this.dialog.widgets.leSDFile.text = "";
	}
	data = settings.value(this.fitImageKey, "");
	if (data !== "") {
		data = (data === "true");
		this.dialog.widgets.chbxFitImage.checked = data;
		this.resizeImage = data;
	}
};

SDFBrowser.prototype.pasteMolecule = function () {
	"use strict";

	var moleculeContent;

	moleculeContent = this.getMoleculeContent();
	molecule.importMolfile(moleculeContent);
};

SDFBrowser.prototype.getFirstMolecule = function () {
	"use strict";

	if (this.filteredMoleculesPosition !== undefined && this.filteredMoleculesPosition.length > 0) {
		this.currentMoleculeFiltered = 1;
		this.currentMolecule = this.filteredMoleculesPosition[this.currentMoleculeFiltered - 1];
	} else {
		this.currentMolecule = 1;
	}

	return this.loadMolecule();
};


SDFBrowser.prototype.getMoleculeByPosition = function (position) {
	"use strict";


	if (position <= this.molCount && position > 0) {
		if (this.filteredMoleculesPosition !== undefined && this.filteredMoleculesPosition.length > 0) {
			this.currentMoleculeFiltered = position;
			this.currentMolecule = this.filteredMoleculesPosition[this.currentMoleculeFiltered - 1];
			return this.loadMolecule();
		}
		this.currentMolecule = position;
		return this.loadMolecule();
	}

	return false;
};

SDFBrowser.prototype.getPreviousMolecule = function () {
	"use strict";

	if (this.filteredMoleculesPosition !== undefined && this.filteredMoleculesPosition.length > 0) {
		if (this.currentMoleculeFiltered > 1) {
			this.currentMoleculeFiltered--;
			this.currentMolecule = this.filteredMoleculesPosition[this.currentMoleculeFiltered - 1];
		}
	} else if (this.currentMolecule > 1) {
		this.currentMolecule--;
	}

	return this.loadMolecule();
};

SDFBrowser.prototype.getNextMolecule = function () {
	"use strict";

	if (this.filteredMoleculesPosition !== undefined && this.filteredMoleculesPosition.length > 0) {
		this.currentMoleculeFiltered++;
		this.currentMolecule = this.filteredMoleculesPosition[this.currentMoleculeFiltered - 1];
		if (this.currentMoleculeFiltered <= this.molCount) {
			return this.loadMolecule();
		}
		this.currentMoleculeFiltered--;
		this.currentMolecule = this.filteredMoleculesPosition[this.currentMoleculeFiltered - 1];
	} else {
		this.currentMolecule++;
		if (this.currentMolecule <= this.molCount) {
			return this.loadMolecule();
		}
		this.currentMolecule--;
	}
};

SDFBrowser.prototype.getLastMolecule = function () {
	"use strict";

	if (this.filteredMoleculesPosition !== undefined && this.filteredMoleculesPosition.length > 0) {
		this.currentMoleculeFiltered = this.molCount;
		this.currentMolecule = this.filteredMoleculesPosition[this.molCount - 1];
	} else if (this.molCount > 0) {
		this.currentMolecule = this.molCount;
	}

	return this.loadMolecule();
};

SDFBrowser.writeToFile = function (aFilePath, aData) {
	"use strict";

	var file, resultFile, result;

	//Write string into a file
	result = false;
	file = new File(aFilePath);
	if (File.exists(aFilePath)) {
		File.remove(aFilePath);
	}
	file.open(File.ReadWrite);
	resultFile = new TextStream(file);
	resultFile.write(aData);
	resultFile.flush();
	file.close();
	if (File.exists(aFilePath)) {
		result = true;
	}

	return result;
};

//Static attribute
SDFBrowser.mnovaIUPACTagName = "Mnova_IUPAC_Name";